/**
 * Copyright (C) 2011  Robin Dietrich, Nils Kenneweg, Leonard Stutz
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 **/ 

#ifndef FACTORY_HPP
#define	FACTORY_HPP

#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp>
#include "datedContainer.hpp"

/** basic creation template */
template <class C> class creator {
    public:
        /** virtual destructor as always */
        virtual ~creator(){}
        /** create function to be extended by deriving class */
        virtual C* create() = 0;
};

/** basic derived creator template which creates a class derived from an abstract type */
template <class dType, class bType> class dCreator : public creator<bType> {
    public:
        bType* create() {
            return new dType;
        }
};

/** standard factory design pattern */
template <class C, typename T> class factory {
    //typedef for function map
    typedef boost::unordered_map<T, creator<C>*> fMapType;
    
    protected:
        /** function map containing all registered objects */
        fMapType FunctionMap;
    public:
        /** add new class to function map */
        void add(T Id, creator<C>* Fn) {
            this->FunctionMap[Id] = Fn;
        }

        /** create a new object, return it's extending type */
        virtual C* create(T Id) {
            return this->FunctionMap[Id]->create();
        }
        
        /** remove an object from the factory */
        void remove(T Id) {
            delete this->FunctionMap[Id].second;
            this->FunctionMap.erase(Id);
        }

        /** virtual destructor as always, no explicit freeing */
        virtual ~factory() {}
};

/** extended factory design pattern with object lifetime and registry like approach */
template <class C, typename T> class datedFactory {
    // typedef for datedContainer on the created class
    typedef datedContainer<C, T> cDatedContainer;
    //typedef for function map
    typedef boost::unordered_map<T, creator<C>*> fMapType;    
    
    protected:
        /** function map containing all registered objects */
        fMapType FunctionMap;
        /** dated container for the block class */
        cDatedContainer creations;
    public:
        /** typedef for a set of available classes */
        typedef std::vector<T> cList;
        
        /** create a new object, return it's extending type */
        boost::shared_ptr<C> create(T Id) {
            if (!this->creations.has(Id)) {
                this->creations.set(Id, this->FunctionMap[Id]->create());
            }
            
            return this->creations.get(Id);
        }       

        /** cleanup the datedContainer */
        void cleanup() {
            this->creations.cleanup();
        }
        
         /** add new class to function map */
        void add(T Id, creator<C>* Fn) {
            this->FunctionMap[Id] = Fn;
        }
        
        /** remove an object from the factory */
        void remove(T Id) {
            delete this->FunctionMap[Id].second;
            this->FunctionMap.erase(Id);
        }
        
        /** returns added classes */
        cList getAvailable() {
            cList tmp;
            typename fMapType::const_iterator it;
            for (it = this->FunctionMap.begin(); it != this->FunctionMap.end(); ++it) {
                tmp.push_back(it->first);
            }
            return tmp;
        }

        /** constructor to allow singleton pattern */
        datedFactory() {};
        /** virtual destructor as always, not explicit freeing */
        virtual ~datedFactory() {
            typename fMapType::iterator it;
            for (it = this->FunctionMap.begin(); it != this->FunctionMap.end(); ++it) {
                delete it->second;
            }
            this->FunctionMap.clear();
        }
};


#endif	/* FACTORY_HPP */